#==========================================================
# 3-D FFT algorithm
#==========================================================

#==========================================================
# Local 3D FFT time
#==========================================================

Bytes__FFT3D_local_once <- function (Sys, n, t) {
  c1 = 10.0
  c2 = 40.0
  c3 = 2.0  # Refined constant for asymptotic approximation

  n_sockets <- getSockets (Sys) (t)
  Z <- getSocketZ (Sys) (t)

  # Local problem size
  M_local <- (n^3) / n_sockets

  if (USE_LOCAL_EXACT) { # Paper Eq #4
    L <- getSocketL (Sys) (t)
    bytes_local <- (n^2 / n_sockets) * c2 * (1 + (n/L) * (1 + log (n, base=Z))) * L
  } else {
    # Asymptotic approximation
    bytes_local <- c3 * c2 * (M_local * log (n, base=Z))
  }
  return (bytes_local)
}

T__FFT3D_local_once <- function (Sys, n, t) {
  beta_mem <- getSocketBW (Sys) (t)
  return (Bytes__FFT3D_local_once (Sys, n, t) / beta_mem)
}

T__FFT3D_local <- function (Sys, n, t) {
  3 * T__FFT3D_local_once (Sys, n, t)
}

#==========================================================
# Local transpose time
#==========================================================

Bytes__FFT3D_local_trans_once <- function (Sys, n, t) {
  n_sockets <- getSockets (Sys) (t)
  return (2*(n^3) / n_sockets)
}

T__FFT3D_local_trans_once <- function (Sys, n, t) {
  beta_mem <- getSocketBW (Sys) (t)
  return (Bytes__FFT3D_local_trans_once (Sys, n, t) / beta_mem)
}

T__FFT3D_local_trans <- function (Sys, n, t) {
  2 * T__FFT3D_local_trans_once (Sys, n, t)
}

#==========================================================
# Network time
#==========================================================

T__FFT3D_net_once <- function (Sys, n, t) {
  t_lat <- 0 # optimistic
#  t_lat <- .05e-6 * sqrt (getSockets (Sys) (t)) # pessimistic
  
  t_bw <- n^3 / (getBisectionBW (Sys) (t))

  return (t_lat + t_bw)
}

T__FFT3D_net <- function (Sys, n, t) {
  2 * T__FFT3D_net_once (Sys, n, t)
}

#==========================================================
# Compute time
#==========================================================
Flops__FFT3D_global <- function (n) {
  return (5 * n^3 * log2 (n))
}

Flops__FFT3D_local_once <- function (Sys, n, t) {
  n_sockets <- getSockets (Sys) (t)
  return (Flops__FFT3D_global (n) / n_sockets)
}

T__FFT3D_flops <- function (Sys, n, t) {
  flops <- Flops__FFT3D_global (n)
  peak <- getPeak (Sys) (t)
  return (3 * flops / peak)
}

#==========================================================
# Total time
#==========================================================

T_intra__FFT3D <- function (Sys, n, t) {
  T__FFT3D_local (Sys, n, t) + T__FFT3D_local_trans (Sys, n, t)
}

T_inter__FFT3D <- function (Sys, n, t) {
  T__FFT3D_net (Sys, n, t)
}

# No overlap
T__FFT3D <- function (Sys, n, t) {
  T_intra__FFT3D (Sys, n, t) + T_inter__FFT3D (Sys, n, t)
}

# Perfect overlap
T_overlap__FFT3D <- function (Sys, n, t) {
  max (T_intra__FFT3D (Sys, n, t), T_inter__FFT3D (Sys, n, t))
}

#==========================================================
# Local flop:byte ratio
#==========================================================
Intensity__FFT3D_local <- function (Sys, n, t) {
  flops <- Flops__FFT3D_local_once (Sys, n, t)
  bytes_fft <- Bytes__FFT3D_local_once (Sys, n, t)
  bytes_trans <- Bytes__FFT3D_local_trans_once (Sys, n, t)
  return (flops / (bytes_fft + bytes_trans))
}

Intensity__FFT3D_local_notrans <- function (Sys, n, t) {
  flops <- Flops__FFT3D_local_once (Sys, n, t)
  bytes_fft <- Bytes__FFT3D_local_once (Sys, n, t)
  return (flops / bytes_fft)
}

#==========================================================
# eof
#==========================================================
